package com.xyz.shiro;

import org.apache.shiro.ShiroException;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class WithIngoresShiroFilterFactoryBean extends ShiroFilterFactoryBean {

    private static final String[] DEF_INGORES = {"mp4","mp4","txt","html","jpg","jpeg","png","pdf","xls","doc"};

    private String[] ingores;

    public String[] getIngores() {
        if(ingores == null || ingores.length == 0){
            return DEF_INGORES;
        }
        return ingores;
    }

    public WithIngoresShiroFilterFactoryBean(){
    }

    public WithIngoresShiroFilterFactoryBean(String[] ingores){
        this.ingores = ingores;
    }

    @Override
    protected AbstractShiroFilter createInstance() throws Exception {

        org.apache.shiro.mgt.SecurityManager securityManager = getSecurityManager();
        if (securityManager == null) {
            String msg = "SecurityManager property must be set.";
            throw new ShiroException(msg);
        }

        if (!(securityManager instanceof WebSecurityManager)) {
            String msg = "The security manager does not implement the WebSecurityManager interface.";
            throw new ShiroException(msg);
        }

        FilterChainManager manager = createFilterChainManager();

        PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
        chainResolver.setFilterChainManager(manager);

        return new WithIngoresShiroFilterFactoryBean.SpringShiroFilter(this.getIngores(), (WebSecurityManager) securityManager, chainResolver);
    }

    private static final class SpringShiroFilter extends AbstractShiroFilter {
        private Set<String> ingores;
        protected SpringShiroFilter(String[] ingores, WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
            if(this.ingores == null){
                this.ingores = new HashSet<>();
            }
            this.ingores.addAll(Arrays.asList(ingores));
            if (webSecurityManager == null) {
                throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
            } else {
                setSecurityManager(webSecurityManager);
                if (resolver != null) {
                    setFilterChainResolver(resolver);
                }
            }
        }

        @Override
        protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws ServletException, IOException {
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            String str = request.getRequestURI().toLowerCase();
            // 因为ShiroFilter 拦截所有请求（在上面我们配置了urlPattern 为 * ，当然你也可以在那里精确的添加要处理的路径，这样就不需要这个类了），而在每次请求里面都做了session的读取和更新访问时间等操作，这样在集群部署session共享的情况下，数量级的加大了处理量负载。
            // 所以我们这里将一些能忽略的请求忽略掉。
            // 当然如果你的集群系统使用了动静分离处理，静态资料的请求不会到Filter这个层面，便可以忽略。
            boolean flag = false;
            int idx = str.indexOf(".");
            if (idx > 0) {
                str = str.substring(idx);
                if (this.ingores.contains(str.toLowerCase()))
                    flag = true;
            }

            if (flag) {
                chain.doFilter(servletRequest, servletResponse);
            } else {
                super.doFilterInternal(servletRequest, servletResponse, chain);
            }
        }
    }

}
